//
//  QR_Bar_ScanView.m
//  QuanJieShopProject
//
//  Created by 刘中义 on 2018/2/28.
//  Copyright © 2018年 apple. All rights reserved.
//

#import "QR_Bar_ScanView.h"
#import <AVFoundation/AVFoundation.h>

NSString *const scanSuccessNotification = @"scanSuccessNotification";
NSString *const scanSuccessMessage = @"scanSuccessMessage";

//位置计算
#define SCANSPACEOFFSET 0.15f
//提示文字
#define REMINDTEXT @"请将二维码/条码放入框内"
//屏幕大小和宽高
#define SCREENBOUNDS [UIScreen mainScreen].bounds
#define SCREENWIDTH CGRectGetWidth([UIScreen mainScreen].bounds)
#define SCREENHEIGHT CGRectGetHeight([UIScreen mainScreen].bounds)

@interface QR_Bar_ScanView () <AVCaptureMetadataOutputObjectsDelegate>
{
    //获取摄像设备
    AVCaptureDevice *device;
}
//会话对象
@property (nonatomic, strong) AVCaptureSession * session;
//输入数据源
@property (nonatomic, strong) AVCaptureDeviceInput * input;
//输出数据源
@property (nonatomic, strong) AVCaptureMetadataOutput * output;
//展示扫描界面
@property (nonatomic, strong) AVCaptureVideoPreviewLayer * scanView;
//有效扫码范围
@property (nonatomic, assign) CGRect scanRect;
//时间
@property (nonatomic, strong) NSTimer *timer;
//线
@property (nonatomic,strong) UIImageView *lineImageView;
//提示文字
@property (nonatomic, strong) UILabel * remindLabel;
//灯光按钮
@property (nonatomic,strong) UIButton *lightButton;

//遮掩层
@property (nonatomic, strong) CAShapeLayer * maskLayer;
//阴影层
@property (nonatomic, strong) CAShapeLayer * shadowLayer;
//扫描框
@property (nonatomic, strong) CAShapeLayer * scanRectLayer;

@end

@implementation QR_Bar_ScanView

#pragma mark -- 初始化，创建扫描界面
+ (instancetype) scanViewShowInController:(UIViewController *)controller{
    
    if (!controller) {
        return nil;
    }
    
    QR_Bar_ScanView *scanView = [[QR_Bar_ScanView alloc]initWithFrame:[UIScreen mainScreen].bounds];
    
    //响应了代理方法
    
    if ([controller conformsToProtocol:@protocol(QRBarScanViewDelegate)]) {
        scanView.delegate = (UIViewController<QRBarScanViewDelegate> *)controller;
    }
    
    return scanView;
}

#pragma mark -- 开辟空间大小
- (instancetype) initWithFrame:(CGRect)frame{
    
    frame = SCREENBOUNDS;
    
    if (self = [super initWithFrame:frame]) {
        
        self.backgroundColor = [UIColor colorWithWhite:0.f alpha:0.2f];
        //扫描界面
        [self.layer addSublayer:self.scanView];
        //扫描范围
        [self setupScanRect];
        //线
        [self addSubview:self.lineImageView];
        //提示文字
        [self addSubview:self.remindLabel];
        //灯光按钮
        [self addSubview:self.lightButton];
        
        self.layer.masksToBounds = YES;
        
    }
    
    return self;
}
#pragma mark -- 设置扫描范围
- (void) setupScanRect{
    CGFloat size = SCREENWIDTH * (1 - 2 * SCANSPACEOFFSET);
    CGFloat minY = (SCREENHEIGHT - size) * 0.5 / SCREENHEIGHT;
    CGFloat maxY = (SCREENHEIGHT + size) * 0.5 / SCREENHEIGHT;
    //rectOfInterest这个属性是以屏幕右上角为坐标原点，并且宽高的顺序要对换过来，右上角和左下角两点的坐标(y1,x1,y2,x2)
    self.output.rectOfInterest = CGRectMake(minY, SCANSPACEOFFSET, maxY, 1 - SCANSPACEOFFSET * 2);
    //阴影层
    [self.layer addSublayer:self.shadowLayer];
    //扫描框
    [self.layer addSublayer:self.scanRectLayer];
}
#pragma mark -- 扫描范围
- (CGRect) scanRect{
    if (CGRectEqualToRect(_scanRect, CGRectZero)) {
        CGRect rectOfInterest = self.output.rectOfInterest;
        CGFloat yOffset = rectOfInterest.size.width - rectOfInterest.origin.x;
        CGFloat xOffset = 1 - 2 * SCANSPACEOFFSET;
        _scanRect = CGRectMake(rectOfInterest.origin.y * SCREENWIDTH, rectOfInterest.origin.x * SCREENHEIGHT, xOffset * SCREENWIDTH, yOffset * SCREENHEIGHT);
    }
    return _scanRect;
}
#pragma mark -- 线
- (UIImageView *) lineImageView{
    
    if (!_lineImageView) {
        //        _lineImageView = [[UIImageView alloc]initWithFrame:CGRectMake(self.scanView.frame.origin.x, self.scanView.frame.origin.y, self.scanView.frame.size.width, 2)];
        _lineImageView = [[UIImageView alloc]initWithFrame:CGRectMake(self.scanRect.origin.x, self.scanRect.origin.y, self.scanRect.size.width, 2)];
        _lineImageView.image = [UIImage imageNamed:@"line"];
    }
    return _lineImageView;
    
}
#pragma mark -- 提示文本
- (UILabel *) remindLabel{
    
    if (!_remindLabel) {
        CGRect textRect = self.scanRect;
        textRect.origin.y += CGRectGetHeight(textRect) + 20;
        textRect.size.height = 25.f;
        
        _remindLabel = [[UILabel alloc]initWithFrame:textRect];
        _remindLabel.font = [UIFont systemFontOfSize:15.f * SCREENWIDTH / 375.f];
        _remindLabel.textAlignment = NSTextAlignmentCenter;
        _remindLabel.text = REMINDTEXT;
        _remindLabel.textColor = [UIColor orangeColor];
        _remindLabel.backgroundColor = [UIColor clearColor];
    }
    
    return _remindLabel;
}
#pragma mark -- 灯光按钮
- (UIButton *) lightButton{
    
    if (!_lightButton) {
        CGRect buttonRect = self.scanRect;
        buttonRect.origin.x += (CGRectGetWidth(buttonRect) - 40) / 2;
        buttonRect.origin.y += CGRectGetHeight(buttonRect) + 60;
        buttonRect.size.width = 40.f;
        buttonRect.size.height= 40.f;
        _lightButton = [[UIButton alloc]initWithFrame:buttonRect];
        [_lightButton setImage:[UIImage imageNamed:@"lightOff"] forState:UIControlStateNormal];
        [_lightButton setImage:[UIImage imageNamed:@"lightOn"] forState:UIControlStateSelected];
        [_lightButton addTarget:self action:@selector(OnOrOffLightAction:) forControlEvents:UIControlEventTouchUpInside];
    }
    
    return _lightButton;
}
#pragma mark -- 开关灯
- (void) OnOrOffLightAction:(UIButton *)sender{
    
    sender.selected = !sender.selected;
    
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
    {
        device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; // 返回用于捕获视频数据的设备（摄像头）
        if (![device hasTorch]) {
            
            UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"当前闪光灯不可用" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil,nil];
            [alert show];
            
        }else{
            
            // 请求独占设备的硬件性能
            [device lockForConfiguration:nil];
            if (device.torchMode == AVCaptureTorchModeOff) {
                // 打开闪光灯
                [device setTorchMode: AVCaptureTorchModeOn];
            }else{
                // 关闭闪光灯
                [device setTorchMode: AVCaptureTorchModeOff];
            }
            
        }
    }
    
}
#pragma mark -- 扫描框
- (CAShapeLayer *)scanRectLayer{
    
    if (!_scanRectLayer) {
        CGRect scanRect = self.scanRect;
        scanRect.origin.x -= 1;
        scanRect.origin.y -= 1;
        scanRect.size.width += 2;
        scanRect.size.height += 2;
        
        _scanRectLayer = [CAShapeLayer layer];
        _scanRectLayer.path = [UIBezierPath bezierPathWithRect:scanRect].CGPath;
        _scanRectLayer.fillColor = [UIColor clearColor].CGColor;
        _scanRectLayer.strokeColor = [UIColor orangeColor].CGColor;
    }
    
    return _scanRectLayer;
}
#pragma mark -- 阴影层
- (CAShapeLayer *) shadowLayer{
    
    if (!_shadowLayer) {
        _shadowLayer = [CAShapeLayer layer];
        _shadowLayer.path = [UIBezierPath bezierPathWithRect:self.bounds].CGPath;
        _shadowLayer.fillColor = [UIColor colorWithWhite:0 alpha:0.75].CGColor;
        _shadowLayer.mask = self.maskLayer;
    }
    return _shadowLayer;
}
#pragma mark -- 遮掩层
- (CAShapeLayer *) maskLayer{
    
    if (!_maskLayer) {
        _maskLayer = [CAShapeLayer layer];
        //生成空缺部分rect的layer
        _maskLayer = [self generateMaskLayerWithRect: SCREENBOUNDS exceptRect: self.scanRect];
    }
    
    return _maskLayer;
}
#pragma mark -- 生成空缺部分rect的layer
- (CAShapeLayer *)generateMaskLayerWithRect: (CGRect)rect exceptRect: (CGRect)exceptRect{
    
    CAShapeLayer * maskLayer = [CAShapeLayer layer];
    if (!CGRectContainsRect(rect, exceptRect)) {
        return nil;
    }else if (CGRectEqualToRect(rect, CGRectZero)) {
        maskLayer.path = [UIBezierPath bezierPathWithRect: rect].CGPath;
        return maskLayer;
    }
    
    CGFloat boundsInitX = CGRectGetMinX(rect);
    CGFloat boundsInitY = CGRectGetMinY(rect);
    CGFloat boundsWidth = CGRectGetWidth(rect);
    CGFloat boundsHeight = CGRectGetHeight(rect);
    
    CGFloat minX = CGRectGetMinX(exceptRect);
    CGFloat maxX = CGRectGetMaxX(exceptRect);
    CGFloat minY = CGRectGetMinY(exceptRect);
    CGFloat maxY = CGRectGetMaxY(exceptRect);
    CGFloat width = CGRectGetWidth(exceptRect);
    
    /** 添加路径*/
    UIBezierPath * path = [UIBezierPath bezierPathWithRect: CGRectMake(boundsInitX, boundsInitY, minX, boundsHeight)];
    [path appendPath: [UIBezierPath bezierPathWithRect: CGRectMake(minX, boundsInitY, width, minY)]];
    [path appendPath: [UIBezierPath bezierPathWithRect: CGRectMake(maxX, boundsInitY, boundsWidth - maxX, boundsHeight)]];
    [path appendPath: [UIBezierPath bezierPathWithRect: CGRectMake(minX, maxY, width, boundsHeight - maxY)]];
    maskLayer.path = path.CGPath;
    
    return maskLayer;
}
#pragma mark -- 会话对象
- (AVCaptureSession *) session{
    
    if (!_session) {
        _session = [[AVCaptureSession alloc]init];
        [_session setSessionPreset:AVCaptureSessionPresetHigh];//高质量采集
        //配置输入输出设置
        [self setupIODevice];
    }
    
    return _session;
}
#pragma mark -- 配置输入输出设置
- (void) setupIODevice{
    
    if ([self.session canAddInput:self.input]) {
        [_session addInput:_input];
    }
    if ([self.session canAddOutput:self.output]) {
        [_session addOutput:_output];
    }
    //二维码,条形码等
    //    [_output setMetadataObjectTypes:@[AVMetadataObjectTypeUPCECode,AVMetadataObjectTypeQRCode,AVMetadataObjectTypeCode39Code,AVMetadataObjectTypeCode39Mod43Code,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeCode93Code, AVMetadataObjectTypeCode128Code,AVMetadataObjectTypePDF417Code,AVMetadataObjectTypeAztecCode,AVMetadataObjectTypeInterleaved2of5Code,AVMetadataObjectTypeITF14Code,AVMetadataObjectTypeDataMatrixCode]];
    _output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeCode128Code];
    
}

#pragma mark -- 视频输入设备
- (AVCaptureDeviceInput *) input{
    
    if (!_input) {
        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        _input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
    }
    
    return _input;
}
#pragma mark -- 数据输出对象
- (AVCaptureMetadataOutput *) output{
    
    if (!_output) {
        _output = [AVCaptureMetadataOutput new];
        [_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    }
    
    return _output;
}
#pragma mark -- 扫描视图
- (AVCaptureVideoPreviewLayer *) scanView{
    
    if (!_scanView) {
        _scanView = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
        _scanView.videoGravity = AVLayerVideoGravityResizeAspectFill;
        _scanView.frame = self.layer.bounds;
        // 将图层插入当前视图
        [self.layer insertSublayer:_scanView atIndex:0];
        //开始捕获
        [self.session startRunning];
    }
    
    return _scanView;
}
#pragma mark -- 开始视频会话
- (void) startScan{
    [self.session startRunning];
    [self startTimer];
}
#pragma mark -- 停止视频会话
- (void) stopScan{
    [self.session stopRunning];
    self.lightButton.selected = NO;
    [self stopTimer];
}
#pragma mark -- 二维码，条形码扫描数据返回 AVCaptureMetadataOutputObjectsDelegate
- (void) captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    
    if (metadataObjects.count > 0) {
        
        [self stopScan];
        AVMetadataMachineReadableCodeObject * metadataObject = metadataObjects[0];
        
        if ([self.delegate respondsToSelector: @selector(scanView:codeInfo:)]) {
            [self.delegate scanView: self codeInfo: metadataObject.stringValue];
            [self removeFromSuperview];
            
        } else {
            
            [[NSNotificationCenter defaultCenter] postNotificationName: scanSuccessNotification object: self userInfo: @{ scanSuccessMessage: metadataObject.stringValue }];
            
        }
    }
    
    
}

#pragma mark -- 点击空白处停止扫描(根据具体情况而定)
- (void) touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    [self stopScan];
    [self removeFromSuperview];
}
#pragma mark -- 释放前停止会话
- (void) dealloc{
    [self stopScan];
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
#pragma mark -- 调用动画方法
- (void)drawRect:(CGRect)rect {
    // Drawing code
    [super drawRect:rect];
    [self startTimer];
}
#pragma mark -- 开始动画
-(void)startTimer{
    if (!_timer) {
        //动画效果
        [self playAnimationAction];
        /* 自动播放 */
        self.timer = [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:@selector(playAnimationAction) userInfo:nil repeats:YES];
    }
    
}
#pragma mark -- 动画效果
- (void) playAnimationAction{
    
    [UIView animateWithDuration:2.5 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        
        //        self.lineImageView .frame = CGRectMake(self.scanView.frame.origin.x, self.scanView.frame.size.height+self.scanView.frame.origin.y, self.scanView.frame.size.width, 2);
        self.lineImageView .frame = CGRectMake(self.scanRect.origin.x, self.scanRect.size.height+self.scanRect.origin.y, self.scanRect.size.width, 2);
        
    } completion:^(BOOL finished) {
        
        //        self.lineImageView .frame = CGRectMake(self.scanView.frame.origin.x, self.scanView.frame.origin.y, self.scanView.frame.size.width, 2);
        self.lineImageView .frame = CGRectMake(self.scanRect.origin.x, self.scanRect.origin.y, self.scanRect.size.width, 2);
        
    }];
    
}
#pragma mark -- 停止动画
- (void)stopTimer
{
    [_timer invalidate];
    _timer = nil;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

@end
